
# ifndef __DFX_AABB_H__
# define __DFX_AABB_H__

# include "dfxVector3.h"

namespace DFX
{
    enum CONTAINMENT_TYPE
    {
        CONTAINMENT_TYPE_VISIBLE,
        CONTAINMENT_TYPE_CULLED,
        CONTAINMENT_TYPE_CLIPPED
    };

    class DFX_EXPORT Aabb
    {
    public:

        Vector3	Min;
        Vector3	Max;

        Aabb()
            : Min(0.0f), Max(0.0f)
        {
        }

        Aabb(const Vector3 &min, const Vector3 &max)
            : Min(min), Max(max)
        {
        }

        Aabb(Float32 minx, Float32 miny, Float32 minz, Float32 maxx, Float32 maxy, Float32 maxz)
            : Min(minx, miny, minz), Max(maxx, maxy, maxz)
        {
        }

        Aabb(Float32 cubeSize)
            : Min(-0.5f * cubeSize), Max(0.5f * cubeSize)
        {
        }

        Aabb(const Aabb &b)
            : Min(b.Min), Max(b.Max)
        {
        }

        inline Float32 GetLengthX() const
        {
            return Max.X - Min.X;
        }

        inline Float32 GetLengthY() const
        {
            return Max.X - Min.X;
        }

        inline Float32 GetLengthZ() const
        {
            return Max.X - Min.X;
        }
        
        inline void Merge(const Aabb &aabb)
        {
            if (aabb.Min.X < Min.X)	Min.X = aabb.Min.X;
            if (aabb.Min.Y < Min.Y)	Min.Y = aabb.Min.Y;
            if (aabb.Min.Z < Min.Z)	Min.Z = aabb.Min.Z;

            if (aabb.Max.X > Max.X)	Max.X = aabb.Max.X;
            if (aabb.Max.Y > Max.Y)	Max.Y = aabb.Max.Y;
            if (aabb.Max.Z > Max.Z)	Max.Z = aabb.Max.Z;
        }

        inline bool IsContained(const Vector3 &v)
        {
            return (v.X >= Min.X && v.X <= Max.X) &&
                (v.Y >= Min.Y && v.Y <= Max.Y) &&
                (v.Z >= Min.Z && v.Z <= Max.Z);
        }

        inline bool IsContained(const Aabb &aabb)
        {
            return Min.X <= aabb.Min.X &&
                Min.Y <= aabb.Min.Y &&
                Min.Z <= aabb.Min.Z &&
                Max.X >= aabb.Max.X &&
                Max.Y >= aabb.Max.Y &&
                Max.Z >= aabb.Max.Z;
        }

        inline bool IsOverlapped(const Aabb &aabb)
        {
            if (aabb.Min.X > Max.X || aabb.Min.Y > Max.Y || aabb.Min.Z > Max.Z)
                return false;

            if (aabb.Max.X < Min.X || aabb.Max.Y < Min.Y || aabb.Max.Z < Min.Z)
                return false;

            return true;
        }

        inline CONTAINMENT_TYPE Contains(const Aabb &aabb)
        {
            if ((Min.X <= aabb.Min.X) && (Min.Y <= aabb.Min.Y) && (Min.Z <= aabb.Min.Z) &&
                (Max.X >= aabb.Max.X) && (Max.Y >= aabb.Max.Y) && (Max.Z >= aabb.Max.Z))
            {
                return CONTAINMENT_TYPE_VISIBLE;
            }

            if ((Max.X < aabb.Min.X) && (Max.Y < aabb.Min.Y) && (Max.Z < aabb.Min.Z) &&
                (Min.X > aabb.Max.X) && (Min.Y > aabb.Max.Y) && (Min.Z > aabb.Max.Z))
            {
                return CONTAINMENT_TYPE_CLIPPED;
            }

            return CONTAINMENT_TYPE_CULLED;
        }

        inline Float32 GetDistToPointSqr(const Vector3 &v)
        {
            Float32 sqrDist = 0.0f;

            for (UInt32 i = 0; i < 3; i++)
            {
                Float32 p = v[i];

                if (p < Min[i]) sqrDist += (Min[i] - p) * (Min[i] - p);
                if (p > Max[i]) sqrDist += (p - Max[i]) * (p - Max[i]);
            }

            return sqrDist;
        }

        inline void GetCorners(Vector3 *v)
        {
            v[0].Set(Max.X, Max.Y, Max.Z);
            v[1].Set(Min.X, Max.Y, Max.Z);
            v[2].Set(Max.X, Min.Y, Max.Z);
            v[3].Set(Min.X, Min.Y, Max.Z);
            v[4].Set(Max.X, Max.Y, Min.Z);
            v[5].Set(Min.X, Max.Y, Min.Z);
            v[6].Set(Max.X, Min.Y, Min.Z);
            v[7].Set(Min.X, Min.Y, Min.Z);
        }
    };
}

# endif